package org.libermundi.theorcs.core.util;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

import org.imgscalr.Scalr;
import org.libermundi.theorcs.core.exceptions.ImageManipulationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ImageUtils {
	private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class);
	
	private ImageUtils() {}
	
	public static BufferedImage resizeImageByWidth(URL imageUrl, int width) throws ImageManipulationException {
		return resizeImageByWidth(loadImage(imageUrl),width);
	}

	private static BufferedImage resizeImageByWidth(BufferedImage originalImage, int width){
		int height = (int)Math.round(width * getRatio(originalImage));
		return resizeImage(originalImage, width, height);
	}

	public static BufferedImage resizeImageByHeight(URL imageUrl, int height) throws ImageManipulationException {
		return resizeImageByHeight(loadImage(imageUrl),height);
	}

	private static BufferedImage resizeImageByHeight(BufferedImage originalImage, int height){
		int width = (int)Math.round(height / getRatio(originalImage));
		
		return resizeImage(originalImage, width, height);
	}
	
	public static BufferedImage resizeImage(URL imageUrl, int width, int height) throws ImageManipulationException {
		return resizeImage(loadImage(imageUrl), width, height);
	}
	
    private static BufferedImage resizeImage(BufferedImage originalImage, int width, int height){
		return Scalr.resize(originalImage, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.FIT_TO_WIDTH, width, height, Scalr.OP_ANTIALIAS);
    }

	public static BufferedImage cropImage(URL imageUrl, int targetWidth, int targetHeight) throws ImageManipulationException {
		return cropImage(loadImage(imageUrl), targetWidth, targetHeight);
	}
	
	private static BufferedImage cropImage(BufferedImage image, int targetWidth, int targetHeight) {
		double avatarWidth = image.getWidth();
		double avatarHeight = image.getHeight();

		double finalWidth = targetWidth;
		double finalHeight = targetHeight;
		int cropX = 0;
		int cropY = 0;
		if (avatarWidth > avatarHeight) {
			finalWidth = Math.floor((targetHeight / avatarHeight)
					* avatarWidth);
			cropX = (int) Math.floor((finalWidth - targetWidth) / 2);
		} else if (avatarWidth < avatarHeight) {
			finalHeight = Math.floor((finalWidth / avatarWidth)
					* avatarHeight);
			cropY = (int) Math.floor((finalHeight - targetHeight) / 2);
		}

		return Scalr.crop(image, cropX, cropY, targetWidth, targetHeight, Scalr.OP_ANTIALIAS);
	}
	
	public static BufferedImage loadImage(URL url) throws ImageManipulationException {
		try {
			return ImageIO.read(url);
		} catch (IOException ioe) {
			logger.error("Failed to load image from URL : " + url.toString(), ioe);
			throw new ImageManipulationException(ioe);
		}		
	}
	
	private static double getRatio(BufferedImage image){
		double originalWidth = image.getWidth();
		double originalHeight = image.getHeight();
		double ratio = originalHeight / originalWidth;
		return ratio;
	}

	/**
	 * @param imageURL
	 * @param width
	 * @param height
	 * @return
	 */
	public static BufferedImage thumbnail(URL imageURL, int width, int height) throws ImageManipulationException {
		return thumbnail(loadImage(imageURL), width, height);
	}
	
	private static BufferedImage thumbnail(BufferedImage image, int width, int height) {
		BufferedImage thumb;
		thumb = fitIn(image, width, height);
		thumb = cropImage(thumb, width, height);
		return thumb;
	}	

	/**
	 * @param imageURL
	 * @param width
	 * @param height
	 * @return
	 */
	public static BufferedImage fitIn(URL imageURL, int width, int height) throws ImageManipulationException {
		return fitIn(loadImage(imageURL),width,height);
	}
	
	private static BufferedImage fitIn(BufferedImage image, int width, int height) {
		BufferedImage fitIn;
		double ratioTmb = (double)height / (double)width;
		if(getRatio(image) > ratioTmb) {
			fitIn = resizeImageByWidth(image, width);
		} else {
			fitIn = resizeImageByHeight(image, height);
		}
		return fitIn;
	}	
}
